//运行
#pragma once
#include "../Comm/log.hpp"
#include "../Comm/util.hpp"
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/resource.h>

namespace ns_runner
{
    using namespace ns_util;
    class Runner
    {

        static void SetResourcesLimit(int cpu_limit, int mem_limit)
        {
            struct rlimit rl;
            rl.rlim_max = RLIM_INFINITY;
            rl.rlim_cur = mem_limit * 1024 * 1024; // MB
            ::setrlimit(RLIMIT_DATA, &rl);  // 6

            //sleep 函数使进程进入休眠状态，不占用 CPU 资源
            rl.rlim_cur = cpu_limit;      // sec
            ::setrlimit(RLIMIT_CPU, &rl); // 24
        }

    public:
        static int Run(const string &file_name, int cpu_limit, int mem_limit)
        {
            // 创建子进程——>程序替换执行可执行文件.exe
            /*本模块只关注:
            1. 是否正常退出(不关注退出码)
            2. 还是收到信号，异常退出（运行时错误）
            */
            // 不做结果是否正确的测试用例

            /*
            返回值：-1 表示内部函数调用失败
                    0 表示正常退出
                    >0（） 信号异常
            */
            pid_t id = fork();
            if (-1 == id)
            {
                LOG(ERROR, "【运行】 fork():%s", strerror(errno));
                return -1;
            }
            else if (0 == id)
            {
                // 子进程
                /*
                 * 对标准输出做重定向到指定文件
                 */
                umask(0);
                int fd_out = open(PathUtil::Stdout(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if (-1 == fd_out)
                {
                    LOG(ERROR, "【运行】 open():%s", strerror(errno));
                    exit(errno);
                }

                int ret1 = dup2(fd_out, 1);
                if (-1 == ret1)
                {
                    LOG(ERROR, "【运行】 dup2():%s", strerror(errno));
                    exit(errno);
                }

                // 设置时空限制
                SetResourcesLimit(cpu_limit, mem_limit);

                // 程序替换
                int ret = execl(PathUtil::Exe(file_name).c_str(), PathUtil::Exe(file_name).c_str(), nullptr);
                if (-1 == ret)
                {
                    LOG(ERROR, "【运行】 execl():%s", strerror(errno));
                    exit(errno);
                }

                exit(-1);
            }
            else
            {
                // 父进程
                int status = 0;
                pid_t ret = waitpid(id, &status, 0);
                if (-1 == ret)
                {
                    LOG(ERROR, "【运行】 waitpid(%d) failed", id);
                    return -1;
                }
                int exit_sig = status & 0x7f;//退出信号
                if (exit_sig) // 运行时异常
                {
                    LOG(INFO, "运行失败 %s 退出信号: %d\n", PathUtil::Exe(file_name).c_str(), exit_sig);
                    return exit_sig;
                }
                else
                {
                    LOG(INFO, "运行成功 %s\n", PathUtil::Exe(file_name).c_str());
                    return 0;
                }
            }
        }
    };
}